﻿using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;

namespace Athena.Runtime.Server.Implementations
{
    using Convertible;
    using Correlation;
    using Utilitiy;
    public class ServiceEntryFactory : IServiceEntryFactory
    {
        private readonly IServiceCorrelationIdGenerator _serviceCorrelationIdGenerator;
        private readonly ITypeConvertibleService _typeConvertibleService;
        private readonly IServiceProvider _serviceProvider;
        //private readonly Dictionary<RuntimeTypeHandle, Type> _serviceImplementaionType;


        public ServiceEntryFactory(IServiceCorrelationIdGenerator serviceCorrelationIdGenerator, ITypeConvertibleService typeConvertibleService, IServiceProvider serviceProvider)
        {
            _serviceCorrelationIdGenerator = serviceCorrelationIdGenerator;
            _typeConvertibleService = typeConvertibleService;
            _serviceProvider = serviceProvider;
            //_serviceImplementaionType = new Dictionary<RuntimeTypeHandle, Type>();
        }

        public IEnumerable<ServiceEntry> CreateServiceEntry(Type serviceInterface, Type serviceImplementaion)
        {
            //_serviceImplementaionType.Add(serviceInterface.TypeHandle, serviceImplementaion);
            foreach (var methodInfo in serviceInterface.GetMethods())
            {
                var implementaionMethodInfo = serviceImplementaion.GetMethod(methodInfo.Name, methodInfo.GetParameters().Select(x => x.ParameterType).ToArray());
                yield return Create(methodInfo, implementaionMethodInfo);
            }
        }

        private ServiceEntry Create(MethodInfo methodInfo, MethodBase implementaionMethod)
        {
            var correlationId = _serviceCorrelationIdGenerator.GenerateCorrelationId(methodInfo);
            var methodInvokerHandler = BaseMethodInvoker.GetMethodInvokerHandler(methodInfo, correlationId);
            return new ServiceEntry
            {
                CorrelationId = correlationId,
                FuncByExecutor = parameters =>
                {

                    var delcaredInstance = _serviceProvider.GetRequiredService(methodInfo.DeclaringType);

                    var methdodParameters = methodInfo.GetParameters();
                    var parameterList = new object[methdodParameters.Length];
                    for (var i = 0; i < methdodParameters.Length; i++)
                    {
                        var parameterInfo = methdodParameters[i];
                        if (parameterInfo.HasDefaultValue && parameters.ContainsKey(parameterInfo.Name))
                        {
                            parameterList[i] = parameterInfo.DefaultValue;
                            continue;
                        }

                        var value = parameters[parameterInfo.Name];
                        var parameter = _typeConvertibleService.Convert(value, parameterInfo.ParameterType);
                        parameterList[i] = parameter;
                    }
                    var result = methodInvokerHandler.Invoke(delcaredInstance,
                        parameterList);

                    return new ValueTask<object>(result);
                }
            };
        }
    }
}
